#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "crypt_eal_cipher.h" // Header file of the interfaces for symmetric encryption and decryption.
#include "bsl_sal.h"
#include "bsl_err.h"
#include "crypt_algid.h" // Algorithm ID list.
#include "crypt_errno.h" // Error code list.

void *StdMalloc(uint32_t len) {
    return malloc((size_t)len);
}

void PrintLastError(void) {
    const char *file = NULL;
    uint32_t line = 0;
    BSL_ERR_GetLastErrorFileLine(&file, &line); // Obtain the name and number of lines of the error file.
    printf("failed at file %s at line %d\n", file, line);
}

int main(void) {
    uint8_t data[10] = {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x1c, 0x14}; // 原始数据
    uint8_t iv[16] = {0}; // 初始化向量 (IV)，通常应为随机值以确保安全，此处全0仅为示例
    uint8_t key[16] = {0}; // AES-128密钥，此处全0仅为示例。也可以是24字节(AES-192)或32字节(AES-256)
    uint32_t dataLen = sizeof(data);
    uint8_t cipherText[100] = {0}; // 存放密文
    uint8_t plainText[100] = {0};  // 存放解密后的明文
    uint32_t outTotalLen = 0;
    uint32_t outLen = sizeof(cipherText);
    uint32_t cipherTextLen;
    int32_t ret;

    printf("Original plain text: ");
    for (uint32_t i = 0; i < dataLen; i++) {
        printf("%02x", data[i]);
    }
    printf("\n");

    // 1. 创建AES-CBC算法上下文
    CRYPT_EAL_CipherCtx *ctx = CRYPT_EAL_CipherNewCtx(CRYPT_CIPHER_AES_CBC);
    if (ctx == NULL) {
        PrintLastError();
        BSL_ERR_DeInit();
        return 1;
    }

    // 2. 初始化上下文为加密模式
    ret = CRYPT_EAL_CipherInit(ctx, key, sizeof(key), iv, sizeof(iv), true);
    if (ret != CRYPT_SUCCESS) {
        printf("Error code: %x\n", ret);
        PrintLastError();
        goto EXIT;
    }

    // 3. 设置PKCS7填充模式
    ret = CRYPT_EAL_CipherSetPadding(ctx, CRYPT_PADDING_PKCS7);
    if (ret != CRYPT_SUCCESS) {
        printf("Error code: %x\n", ret);
        PrintLastError();
        goto EXIT;
    }

    // 4. 加密数据
    ret = CRYPT_EAL_CipherUpdate(ctx, data, dataLen, cipherText, &outLen);
    if (ret != CRYPT_SUCCESS) {
        printf("Error code: %x\n", ret);
        PrintLastError();
        goto EXIT;
    }
    outTotalLen += outLen;
    outLen = sizeof(cipherText) - outTotalLen;

    // 5. 结束加密，处理可能的最后一个数据块和填充
    ret = CRYPT_EAL_CipherFinal(ctx, cipherText + outTotalLen, &outLen);
    if (ret != CRYPT_SUCCESS) {
        printf("Error code: %x\n", ret);
        PrintLastError();
        goto EXIT;
    }
    outTotalLen += outLen;
    cipherTextLen = outTotalLen;

    printf("Cipher text: ");
    for (uint32_t i = 0; i < outTotalLen; i++) {
        printf("%02x", cipherText[i]);
    }
    printf("\n");

    // 6. 重新初始化上下文为解密模式
    ret = CRYPT_EAL_CipherInit(ctx, key, sizeof(key), iv, sizeof(iv), false);
    if (ret != CRYPT_SUCCESS) {
        printf("Error code: %x\n", ret);
        PrintLastError();
        goto EXIT;
    }

    // 7. 设置相同的填充模式
    ret = CRYPT_EAL_CipherSetPadding(ctx, CRYPT_PADDING_PKCS7);
    if (ret != CRYPT_SUCCESS) {
        printf("Error code: %x\n", ret);
        PrintLastError();
        goto EXIT;
    }

    outTotalLen = 0;
    outLen = sizeof(plainText);

    // 8. 解密密文
    ret = CRYPT_EAL_CipherUpdate(ctx, cipherText, cipherTextLen, plainText, &outLen);
    if (ret != CRYPT_SUCCESS) {
        printf("Error code: %x\n", ret);
        PrintLastError();
        goto EXIT;
    }
    outTotalLen += outLen;
    outLen = sizeof(plainText) - outTotalLen;

    // 9. 结束解密，移除填充
    ret = CRYPT_EAL_CipherFinal(ctx, plainText + outTotalLen, &outLen);
    if (ret != CRYPT_SUCCESS) {
        printf("Error code: %x\n", ret);
        PrintLastError();
        goto EXIT;
    }
    outTotalLen += outLen;

    printf("Decrypted plain text: ");
    for (uint32_t i = 0; i < outTotalLen; i++) {
        printf("%02x", plainText[i]);
    }
    printf("\n");

    // 10. 验证解密结果是否与原始数据一致
    if (outTotalLen != dataLen || memcmp(plainText, data, dataLen) != 0) {
        printf("Verification failed!\n");
        ret = -1; // 设置错误码
        goto EXIT;
    }
    printf("AES-CBC encryption and decryption successful!\n");

EXIT:
    // 11. 释放上下文
    CRYPT_EAL_CipherFreeCtx(ctx);
    BSL_ERR_DeInit();
    return ret;
}
